package com.canaan.common.encrypt;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.util.StringUtils;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ReadListener;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * @Author du.tang
 * @Date 2021/5/14 19:21
 * @Version 1.0.0
 * @Description 重写请求体
 */
@Slf4j
@Order(Integer.MIN_VALUE)
public class HttpRequestDecodeFilter implements Filter {

    public static final String PARAM_Q = "q";
    public static final String PARAM_CID = "cid";
    public static final String PARAM_SIGN = "sign";

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //参数判断
        if (StringUtils.isEmpty(servletRequest.getParameter(PARAM_Q))
                || StringUtils.isEmpty(servletRequest.getParameter(PARAM_CID))
                || StringUtils.isEmpty(servletRequest.getParameter(PARAM_SIGN))) {
            log.info("参数验证错误 params:{},url:{}", servletRequest.getParameterMap(), servletRequest.getServletContext().getContextPath());
            throw new IllegalArgumentException("参数验证错误");
        }
        log.info("doFilter cid:{},q:{},sign:{}", servletRequest.getParameter(PARAM_CID), servletRequest.getParameter(PARAM_Q), servletRequest.getParameter(PARAM_SIGN));
        //验证签名
        validSign(servletRequest, AesCryptorUtils.SECURITY_KEY);

        //修改请求体(解密)
        DecodeHttpServletRequestWrapper requestWrapper = new DecodeHttpServletRequestWrapper((HttpServletRequest) servletRequest, AesCryptorUtils.SECURITY_KEY);
        filterChain.doFilter(requestWrapper, servletResponse);
    }

    /**
     * 验证签名信息
     *
     * @param servletRequest 请求
     * @param securityKey    密钥
     */
    void validSign(ServletRequest servletRequest, String securityKey) {
        SortedMap<String, String> params = new TreeMap<>();
        params.put(PARAM_CID, servletRequest.getParameter(PARAM_CID));
        params.put(PARAM_Q, servletRequest.getParameter(PARAM_Q));
        String sign = SignUtils.doMd5Sign(SignUtils.getSignStr(params) + securityKey);
        if (!sign.equals(servletRequest.getParameter(PARAM_SIGN))) {
            log.error("签名验证错误 params:{},url:{}", params, servletRequest.getServletContext().getContextPath());
            throw new IllegalArgumentException("签名验证错误");
        }
    }

    @Getter
    public class DecodeHttpServletRequestWrapper extends HttpServletRequestWrapper {
        private final String requestBody;
        private final String securityKey;

        DecodeHttpServletRequestWrapper(HttpServletRequest request, String securityKey) {
            super(request);
            this.securityKey = securityKey;
                this.requestBody = AesCryptorUtils.decrypt(request.getParameter(PARAM_Q), securityKey);
            this.setAttribute(PARAM_CID.toUpperCase(), request.getParameter(PARAM_CID));
        }

        @Override
        public ServletInputStream getInputStream() {
            final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(requestBody.getBytes());
            return new ServletInputStream() {
                @Override
                public boolean isReady() {
                    return false;
                }

                @Override
                public int readLine(byte[] b, int off, int len) throws IOException {
                    return super.readLine(b, off, len);
                }

                @Override
                public boolean isFinished() {
                    return false;
                }

                @Override
                public void setReadListener(ReadListener readListener) {

                }

                @Override
                public int read() {
                    return byteArrayInputStream.read();
                }
            };
        }

        @Override
        public BufferedReader getReader() {
            return new BufferedReader(new InputStreamReader(this.getInputStream()));
        }
    }
}
